package com.uuah.server.transport.service;

import java.util.Properties;

import com.uuah.exception.lifecycle.InitialisationException;
import com.uuah.server.api.component.service.IService;
import com.uuah.server.api.config.registry.IRegistry;
import com.uuah.server.api.endpoint.IEndpointURIBuilder;
import com.uuah.server.api.endpoint.IUuahEndpoint;
import com.uuah.server.api.session.ISessionHandler;
import com.uuah.server.api.transformer.IBaseTransformer;
import com.uuah.server.api.transport.IConnector;
import com.uuah.server.api.transport.IMessageAdapter;
import com.uuah.server.api.transport.IMessageDispatcherFactory;
import com.uuah.server.api.transport.IMessageReceiver;
import com.uuah.server.api.transport.service.ITransportServiceDescriptor;
import com.uuah.server.config.Constants;
import com.uuah.server.config.i18n.ServerMessages;
import com.uuah.server.config.registry.AbstractServiceDescriptor;
import com.uuah.server.endpoint.UrlEndpointURIBuilder;
import com.uuah.server.exception.AbstractUuahServerException;
import com.uuah.server.exception.transport.service.TransportFactoryException;
import com.uuah.server.exception.transport.service.TransportServiceException;
import com.uuah.server.session.UuahSessionHandler;
import com.uuah.server.transport.NullPayload;
import com.uuah.utils.ClassUtils;

/**
 *
 * TODO comment ...
 *
 * <p>
 * update record:updator，update time，update content and version
 * </p>
 *
 * @author <a href="jonny_quan@hotmail.com">jonny</a>
 * @date 2009 2009-3-27 下午02:48:54
 * @version 1.0.0
 */
public class TransportServiceDescriptor extends AbstractServiceDescriptor
		implements ITransportServiceDescriptor {
	private String connector;
	private String dispatcherFactory;
	private String messageAdapter;
	private String messageReceiver;
	private String endpointBuilder;
	private String sessionHandler;
	private String defaultRequestTransformer;
	private String defaultResponseTransformer;

	private IBaseTransformer requestTransformer;
	private IBaseTransformer responseTransformer;

	private ClassLoader classLoader;

	public TransportServiceDescriptor(String service, Properties props,
			IRegistry registry, ClassLoader classLoader) {
		super(service);

		connector = removeProperty(Constants.CONNECTOR_CLASS, props);
		dispatcherFactory = removeProperty(
				Constants.CONNECTOR_DISPATCHER_FACTORY, props);
		messageReceiver = removeProperty(
				Constants.CONNECTOR_MESSAGE_RECEIVER_CLASS, props);
		messageAdapter = removeProperty(Constants.CONNECTOR_MESSAGE_ADAPTER,
				props);
		defaultRequestTransformer = removeProperty(
				Constants.CONNECTOR_REQUEST_TRANSFORMER, props);
		defaultResponseTransformer = removeProperty(
				Constants.CONNECTOR_RESPONSE_TRANSFORMER, props);
		endpointBuilder = removeProperty(Constants.CONNECTOR_ENDPOINT_BUILDER,
				props);
		sessionHandler = removeProperty(Constants.CONNECTOR_SESSION_HANDLER,
				props);

		this.classLoader = classLoader;
	}

	public void setOverrides(Properties props) {
		if (props == null || props.size() == 0) {
			return;
		}

		connector = props.getProperty(Constants.CONNECTOR_CLASS, connector);
		dispatcherFactory = props.getProperty(
				Constants.CONNECTOR_DISPATCHER_FACTORY, dispatcherFactory);
		messageReceiver = props.getProperty(
				Constants.CONNECTOR_MESSAGE_RECEIVER_CLASS, messageReceiver);

		messageAdapter = props.getProperty(Constants.CONNECTOR_MESSAGE_ADAPTER,
				messageAdapter);

		String temp = props
				.getProperty(Constants.CONNECTOR_REQUEST_TRANSFORMER);
		if (temp != null) {
			defaultRequestTransformer = temp;
			requestTransformer = null;
		}

		temp = props.getProperty(Constants.CONNECTOR_RESPONSE_TRANSFORMER);
		if (temp != null) {
			defaultResponseTransformer = temp;
			responseTransformer = null;
		}

		temp = props.getProperty(Constants.CONNECTOR_ENDPOINT_BUILDER);
		if (temp != null) {
			endpointBuilder = temp;
		}
	}

	public IMessageAdapter createMessageAdapter(Object message)
			throws TransportServiceException {
		return createMessageAdapter(message, messageAdapter);
	}

	protected IMessageAdapter createMessageAdapter(Object message, String clazz)
			throws TransportServiceException {
		if (message == null) {
			message = NullPayload.getInstance();
		}
		if (messageAdapter != null) {
			try {
				return (IMessageAdapter) ClassUtils.instanciateClass(clazz,
						new Object[] { message }, classLoader);
			} catch (Exception e) {
				throw new TransportServiceException(ServerMessages
						.failedToCreateObjectWith("Message Adapter", clazz), e);
			}
		} else {
			throw new TransportServiceException(ServerMessages
					.objectNotSetInService("Message Adapter", getService()));
		}
	}

	public ISessionHandler createSessionHandler()
			throws TransportServiceException {
		if (sessionHandler == null) {
			sessionHandler = UuahSessionHandler.class.getName();
			if (logger.isDebugEnabled()) {
				logger
						.debug("No session.handler set in service description, defaulting to: "
								+ sessionHandler);
			}
		}
		try {
			return (ISessionHandler) ClassUtils.instanciateClass(
					sessionHandler, ClassUtils.NO_ARGS, classLoader);
		} catch (Throwable e) {
			throw new TransportServiceException(
					ServerMessages.failedToCreateObjectWith("SessionHandler",
							sessionHandler), e);
		}
	}

	public IMessageReceiver createMessageReceiver(IConnector connector,
			IService service, IUuahEndpoint endpoint)
			throws AbstractUuahServerException {

		IMessageReceiver mr = createMessageReceiver(connector, service,
				endpoint, null);
		try {
			mr.initialise();
		} catch (InitialisationException e) {
			throw new AbstractUuahServerException(e);
		}
		return mr;
	}

	public IMessageReceiver createMessageReceiver(IConnector connector,
			IService service, IUuahEndpoint endpoint, Object[] args)
			throws AbstractUuahServerException {
		String receiverClass = messageReceiver;

		if (receiverClass != null) {
			Object[] newArgs;

			if (args != null && args.length != 0) {
				newArgs = new Object[3 + args.length];
			} else {
				newArgs = new Object[3];
			}

			newArgs[0] = connector;
			newArgs[1] = service;
			newArgs[2] = endpoint;

			if (args != null && args.length != 0) {
				System.arraycopy(args, 0, newArgs, 3, newArgs.length - 3);
			}

			try {
				IMessageReceiver mr = (IMessageReceiver) ClassUtils
						.instanciateClass(receiverClass, newArgs, classLoader);
				mr.initialise();
				return mr;
			} catch (Exception e) {
				throw new TransportServiceException(ServerMessages
						.failedToCreateObjectWith("Message Receiver",
								getService()), e);
			}
		} else {
			throw new TransportServiceException(ServerMessages
					.objectNotSetInService("Message Receiver", getService()));
		}
	}

	public IMessageDispatcherFactory createDispatcherFactory()
			throws TransportServiceException {
		if (dispatcherFactory != null) {
			try {
				return (IMessageDispatcherFactory) ClassUtils.instanciateClass(
						dispatcherFactory, ClassUtils.NO_ARGS, classLoader);
			} catch (Exception e) {
				throw new TransportServiceException(ServerMessages
						.failedToCreateObjectWith("Message Dispatcher Factory",
								dispatcherFactory), e);
			}
		} else {
			return null;
		}
	}

	public IConnector createConnector() throws TransportServiceException {
		IConnector newConnector;
		try {
			if (connector != null) {
				Class connectorClass;
				if (classLoader != null) {
					connectorClass = ClassUtils.loadClass(connector,
							classLoader);
				} else {
					connectorClass = ClassUtils
							.loadClass(connector, getClass());
				}
				newConnector = (IConnector) connectorClass.newInstance();
			} else {
				throw new TransportServiceException(ServerMessages
						.objectNotSetInService("Connector", getService()));
			}
		} catch (TransportServiceException e) {
			throw e;
		} catch (Exception e) {
			throw new TransportServiceException(ServerMessages
					.failedToCreateObjectWith("Connector", connector), e);
		}

		if (newConnector.getName() == null) {
			newConnector.setName("_" + newConnector.getProtocol()
					+ "Connector#" + connector.hashCode());
		}
		return newConnector;
	}

	public IBaseTransformer createRequestTransformer()
			throws TransportFactoryException {
		if (requestTransformer != null) {
			return requestTransformer;
		}
		if (defaultRequestTransformer != null) {
			logger.info("Loading default request transformer: "
					+ defaultRequestTransformer);
			try {
				requestTransformer = (IBaseTransformer) ClassUtils
						.instanciateClass(defaultRequestTransformer,
								ClassUtils.NO_ARGS, classLoader);
				return requestTransformer;
			} catch (Exception e) {
				throw new TransportFactoryException(ServerMessages
						.failedToLoadTransformer("request",
								defaultRequestTransformer), e);
			}
		}
		return null;
	}

	public IBaseTransformer createResponseTransformer()
			throws TransportFactoryException {
		if (responseTransformer != null) {
			return responseTransformer;
		}
		if (defaultResponseTransformer != null) {
			logger.info("Loading default response transformer: "
					+ defaultResponseTransformer);
			try {
				responseTransformer = (IBaseTransformer) ClassUtils
						.instanciateClass(defaultResponseTransformer,
								ClassUtils.NO_ARGS, classLoader);
				return responseTransformer;
			} catch (Exception e) {
				throw new TransportFactoryException(ServerMessages
						.failedToLoadTransformer("response",
								defaultResponseTransformer), e);
			}
		}
		return null;
	}

	public IEndpointURIBuilder createEndpointBuilder()
			throws TransportFactoryException {
		if (endpointBuilder == null) {
			logger
					.debug("Endpoint resolver not set, Loading default resolver: "
							+ UrlEndpointURIBuilder.class.getName());
			return new UrlEndpointURIBuilder();
		} else {
			logger.debug("Loading endpointUri resolver: " + endpointBuilder);
			try {
				return (IEndpointURIBuilder) ClassUtils.instanciateClass(
						endpointBuilder, ClassUtils.NO_ARGS, classLoader);
			} catch (Exception e) {
				throw new TransportFactoryException(ServerMessages
						.failedToLoad("Endpoint Builder: " + endpointBuilder),
						e);
			}
		}
	}
}
